-- ------------------ --
-- Blizzard Variables --
-- ------------------ --

local BOOKTYPE_SPELL = BOOKTYPE_SPELL
local GetSpellName = GetSpellName
local tremove = table.remove
local tinsert = table.insert
local pairs = pairs
local ipairs = ipairs

-- --------------- --
-- AddOn Variables --
-- --------------- --

local parentTbl = {}
local childTbl = {}
local backgroundTexture = "Interface\\Icons\\Spell_Holy_WordFortitude"
local reverse = {TOP = "BOTTOM", BOTTOM = "TOP", LEFT = "RIGHT", RIGHT = "LEFT"}

-- ---------------------- --
-- SecureHeader Functions --
-- ---------------------- --

local function SetupHeader(frame)
	frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")

	local header = CreateFrame("Button", nil, UIParent, "SecureHandlerBaseTemplate")
	frame.header = header
	frame:SetParent(header)

	header:Execute([[
		expandEvent = "OnMouseOver"
		counter = 0
		toggledOn = false
	]])

	header:WrapScript(frame, "OnClick", [[
		if expandEvent == "OnRightClick" then
			if toggledOn then
				control:ChildUpdate("hide")
				toggledOn = false
			else
				control:ChildUpdate("show")
				toggledOn = true
			end
		end
	]])

	header:WrapScript(frame, "OnEnter", [[
		if expandEvent == "OnMouseOver" then
			control:ChildUpdate("show")
		end
	]])

	header:WrapScript(frame, "OnLeave", [[
		if expandEvent == "OnMouseOver" then
			control:SetAnimating(true)
		end
	]])

	header:SetAttribute("_onupdate", [[
		counter = counter + elapsed
		if self:IsUnderMouse(true) then
			counter = 0
		elseif counter > 0.4 then
			counter = 0
			control:SetAnimating(false)
			control:ChildUpdate("hide")
		end
	]])
end

-- ------------------- --
-- Frame Script Events --
-- ------------------- --

local function onEnter(self)
	if self.id then
		if GetCVar("UberTooltips") == "1" then
			GameTooltip_SetDefaultAnchor(GameTooltip, self)
			GameTooltip:SetSpell(self.id, BOOKTYPE_SPELL)
		else
			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
			GameTooltip:SetText(GetSpellName(self.id, BOOKTYPE_SPELL))
		end
		if self.header then
			GameTooltip:AddLine(L["Hold ALT to drag this button."], 0, 1, 1)
			GameTooltip:Show()
		end
	end
end

local function onLeave()
	GameTooltip:Hide()
end

local function onEvent(self, event, ...)
	if event == "SPELL_UPDATE_COOLDOWN" then
		for i = 1, self.numButtons do
			local id = self.button[i].id
			if id then
				local start, duration = GetSpellCooldown(id, BOOKTYPE_SPELL)
				if start > 0 and duration > 0 then
					self.button[i].cooldown:SetCooldown(start, duration)
				end
			end
		end
	elseif event == "PLAYER_LOGIN" then
		if self.mustInit and self.Initialize then
			self:Initialize()
			self.mustInit = false
		end
	end
	if self.OnEvent then
		self:OnEvent(event, ...)
	end
end

local function updateLayout(self)
	local lastRowButton = 1
	local counter = 1
	for i = 2, self.numButtons do
		self.button[i]:ClearAllPoints()
		if counter >= self.perRow then
			self.button[i]:SetPoint(reverse[self.vertical], self.button[lastRowButton], self.vertical, 0, 0)
			lastRowButton = i
			counter = 1
		else
			self.button[i]:SetPoint(reverse[self.horizontal], self.button[i-1], self.horizontal, 0, 0)
			counter = counter + 1
		end
	end
end

function ZSpellButtonTemplates_Child_OnLoad(self)
	local name = self:GetName()
	self.icon = getglobal(name.."Icon")
	self.background = getglobal(name.."Background")
	self.cooldown = getglobal(name.."Cooldown")
	if self.header then
		self:HookScript("OnEnter", onEnter)
		self:HookScript("OnLeave", onLeave)
	else
		self:SetScript("OnEnter", onEnter)
		self:SetScript("OnLeave", onLeave)
	end
	self.UpdateTooltip = onEnter
	for i, v in pairs(childTbl) do
		self[i] = v
	end
	self:SetAttribute("type1", "spell")

	local header = self:GetParent()

	header:WrapScript(self, "OnClick", [[
		control:ChildUpdate("hide")
	]])

	if self.header then return end

	self:SetAttribute("_childupdate-show", [[
		self:Show()
	]])

	self:SetAttribute("_childupdate-hide", [[
		if expandEvent ~= "Always" then
			self:Hide()
		end
	]])
end

function ZSpellButtonTemplates_Parent_OnLoad(self)
	self.parent = self
	SetupHeader(self)
	self:SetScript("OnEvent", onEvent)
	self:RegisterForDrag("LeftButton")
	self:RegisterEvent("SPELL_UPDATE_COOLDOWN")
	self:RegisterEvent("PLAYER_LOGIN")
	self.mustInit = true
	self.numButtons = 1
	self.button = {self}
	self.bgColor = {r = 0.5, g = 0.4, b = 0.2}
	self.count = 99
	self.size = 30
	self.roundButton = false
	self.horizontal = "RIGHT"
	self.vertical = "BOTTOM"
	self.perRow = 99
	self.spellList = {}
	self.using = 0
	for i, v in pairs(parentTbl) do
		self[i] = v
	end
	ZSpellButtonTemplates_Child_OnLoad(self)
end

-- ----------------- --
-- Spell Alterations --
-- ----------------- --

function parentTbl:GetSpellId(spell)
	local _, _, offset, numSpells = GetSpellTabInfo(GetNumSpellTabs())
	local start = offset + numSpells
	for i = start, 1, -1 do
		if GetSpellName(i, BOOKTYPE_SPELL) == spell then
			return i
		end
	end
end

local function CreateButton(parent)
	parent.numButtons = parent.numButtons + 1
	local child = CreateFrame("Button", parent:GetName()..parent.using, parent.header, "ZSpellButtonTemplate")
	child.parent = parent
	parent.button[parent.numButtons] = child
	parent:SetSize()
	parent:SetCount()
	parent:SetExpandType()
	parent:SetRoundButton()
	parent:SetCooldownDisplay()
	parent:SetBackgroundColor()
	parent:SetHorizontal()
	parent:SetVertical()
	parent:SetPerRow()
	return child
end

local function GetButton(parent)
	parent.using = parent.using + 1
	if parent.numButtons >= parent.using then
		return parent.button[parent.using]
	else

		return CreateButton(parent)
	end
end

function parentTbl:AddSpell(name)
	tinsert(self.spellList, name)
	local id = self:GetSpellId(name)
	if id then
		local button = GetButton(self)
		button:SetAttribute("spell1", name)
		button.id = id
		button.spell = name
		button:SetIcon(GetSpellTexture(id, BOOKTYPE_SPELL))
		if self.OnEvent then
			self:OnEvent("SPELLBUTTON_CHANGED", self.using, name)
		end
		self:Show()
		self.hasSpells = true
	end
end

function parentTbl:ResetSpells()
	for i = 1, #self.spellList do
		tremove(self.spellList)
	end
	if self.special then
		self.using = 1
	else
		self.using = 0
	end
	self.hasSpells = false
end

local function refreshSpells(parent)
	local temp = {}
	for i, v in ipairs(parent.spellList) do
		temp[i] = v
	end
	parent:ResetSpells()
	for i = 1, #temp do
		parent:AddSpell(temp[i])
	end
	parent:SetCount()
end

-- ---------------------- --
-- Child Setter Functions --
-- ---------------------- --

function childTbl:SetIcon(value)
	if not value then return end
	self.lastTexture = value
	if self.parent.roundButton then
		SetPortraitToTexture(self.icon, value)
	else
		self.icon:SetTexture(value)
	end
end

-- ----------------------- --
-- Parent Setter Functions --
-- ----------------------- --

function parentTbl:SetSpecial(value)
	self.special = value or self.special or false
	if self.special then
		self:SetAttribute("type1", "macro")
	else
		self:SetAttribute("type1", "spell")
	end
	refreshSpells(self)
end

function parentTbl:SetExpandType(value)
	self.expandType = value or self.expandType or "OnMouseOver"
	self.header:Execute([[
		expandEvent = "]] .. self.expandType .. [["
		if expandEvent == "Always" then
			control:ChildUpdate("show")
		else
			control:ChildUpdate("hide")
		end
	]])
end

function parentTbl:SetCount(value)
	self.count = value or self.count or 99
	local check = self.expandType == "Always"
	for i = 2, self.numButtons do
		-- Hide extra buttons
		if i > self.count or i > self.using then
			self.button[i]:SetAttribute("_childupdate-show", nil)
			self.button[i]:Hide()
		-- Display the number of buttons requested
		else
			self.button[i]:SetAttribute("_childupdate-show", "self:Show()")
			if check then
				self.button[i]:Show()
			end
		end
	end
	if self.count == 0 then
		self:Hide()
	elseif self.hasSpells then
		self:Show()
	end
end

function parentTbl:SetSize(value)
	self.size = value or self.size or 30
	for i = 1, self.numButtons do
		self.button[i]:SetWidth(self.size)
		self.button[i]:SetHeight(self.size)
	end
end

function parentTbl:SetRoundButton(value)
	if type(value) == "boolean" then
		self.roundButton = value
	else
		self.roundButton = self.roundButton or false
	end
	if self.roundButton then
		for i = 1, self.numButtons do
			SetPortraitToTexture(self.button[i].background, backgroundTexture)
			SetPortraitToTexture(self.button[i].icon, self.button[i].lastTexture)
		end
	else
		for i = 1, self.numButtons do
			self.button[i].background:SetTexture(backgroundTexture)
			self.button[i].icon:SetTexture(self.button[i].lastTexture)
		end
	end
end

function parentTbl:SetCooldownDisplay(value)
	self.cooldownDisplay = value or self.cooldownDisplay or false
end

function parentTbl:SetBackgroundColor(value)
	self.bgColor.r = value and value.r or self.bgColor.r or 0.5
	self.bgColor.g = value and value.g or self.bgColor.g or 0.4
	self.bgColor.b = value and value.b or self.bgColor.b or 0.2
	for i = 1, self.numButtons do
		self.button[i].background:SetVertexColor(self.bgColor.r, self.bgColor.g, self.bgColor.b)
	end
end

function parentTbl:SetHorizontal(value)
	self.horizontal = value or self.horizontal or "RIGHT"
	updateLayout(self)
end

function parentTbl:SetVertical(value)
	self.vertical = value or self.vertical or "BOTTOM"
	updateLayout(self)
end

function parentTbl:SetPerRow(value)
	self.perRow = value or self.perRow or 99
	updateLayout(self)
end